package com.oracle2mysql.migration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;

/**
 * HTML差异比较生成器，用于生成源文件和处理后文件的对比报告
 */
public class HtmlDiffGenerator {

    private static final Logger logger = LoggerFactory.getLogger(HtmlDiffGenerator.class);

    /**
     * 生成HTML比较报告
     * 
     * @param sourceFiles 源文件列表
     * @param targetFiles 目标文件列表
     * @param outputHtmlPath 输出HTML文件路径
     * @throws IOException 当IO操作失败时抛出
     */
    public void generateDiffReport(List<FilePair> filePairs, String outputHtmlPath) throws IOException {
        logger.info("开始生成HTML对比报告: {}", outputHtmlPath);
        
        // 创建输出目录
        File outputFile = new File(outputHtmlPath);
        File parentDir = outputFile.getParentFile();
        if (parentDir != null && !parentDir.exists()) {
            parentDir.mkdirs();
        }
        
        // 生成HTML内容
        String htmlContent = generateHtmlContent(filePairs);
        
        // 写入文件
        FileUtils.writeStringToFile(outputFile, htmlContent, StandardCharsets.UTF_8);
        
        logger.info("HTML对比报告已生成: {}", outputHtmlPath);
    }

    /**
     * 生成HTML内容
     */
    private String generateHtmlContent(List<FilePair> filePairs) throws IOException {
        StringBuilder html = new StringBuilder();
        
        // HTML头部
        html.append("<!DOCTYPE html>");
        html.append("<html lang=\"zh-CN\">");
        html.append("<head>");
        html.append("<meta charset=\"UTF-8\">");
        html.append("<title>Oracle到MySQL迁移对比报告</title>");
        html.append("<style>");
        html.append("body { font-family: 'Microsoft YaHei', Arial, sans-serif; margin: 0; padding: 20px; background-color: #f5f5f5; }");
        html.append(".container { width: 100%; max-width: 1400px; margin: 0 auto; background-color: white; padding: 20px; box-shadow: 0 0 10px rgba(0,0,0,0.1); box-sizing: border-box; }");
        html.append("h1 { color: #333; text-align: center; margin-bottom: 10px; }");
        html.append(".info { margin-bottom: 20px; color: #666; text-align: center; }");
        html.append(".file-tabs { margin-bottom: 20px; border-bottom: 1px solid #ddd; overflow-x: auto; white-space: nowrap; }");
        html.append(".tab-button { display: inline-block; padding: 10px 20px; background-color: #e0e0e0; border: none; cursor: pointer; border-radius: 5px 5px 0 0; margin-right: 2px; transition: background-color 0.2s; }");
        html.append(".tab-button:hover { background-color: #d0d0d0; }");
        html.append(".tab-button.active { background-color: #fff; border: 1px solid #ddd; border-bottom-color: transparent; font-weight: bold; position: relative; top: 1px; }");
        html.append(".tab-content { display: none; border: 1px solid #ddd; border-radius: 0 0 5px 5px; padding: 0; }");
        html.append(".tab-content.active { display: block; }");
        html.append(".diff-container { display: flex; width: 100%; }");
        html.append(".diff-column { flex: 1; padding: 0; overflow-x: auto; min-height: 500px; }");
        html.append(".diff-column:first-child { border-right: 2px solid #ddd; }");
        html.append(".column-header { background-color: #f0f0f0; padding: 12px; font-weight: bold; border-bottom: 1px solid #ddd; text-align: center; font-size: 14px; }");
        html.append(".diff-content { padding: 15px; }");
        html.append("pre { margin: 0; white-space: pre-wrap; word-wrap: break-word; font-family: 'Courier New', monospace; font-size: 14px; line-height: 1.5; }");
        html.append(".sql-keyword { color: #0000ff; font-weight: bold; }");
        html.append(".sql-string { color: #008000; }");
        html.append(".sql-comment { color: #808080; font-style: italic; }");
        html.append(".sql-tag { color: #a31515; }");
        html.append(".changed-line { background-color: #fffacd; padding: 2px 5px; border-radius: 3px; }");
        html.append("</style>");
        html.append("</head>");
        html.append("<body>");
        html.append("<div class=\"container\">");
        html.append("<h1>Oracle到MySQL迁移对比报告</h1>");
        html.append("<div class=\"info\">生成时间: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "</div>");
        
        // 文件标签
        html.append("<div class=\"file-tabs\">");
        for (int i = 0; i < filePairs.size(); i++) {
            FilePair pair = filePairs.get(i);
            String fileName = pair.getSourceFile().getName();
            html.append("<button class=\"tab-button" + (i == 0 ? " active" : "") + "\" onclick=\"openTab(event, 'tab" + i + "')\">").append(fileName).append("</button>");
        }
        html.append("</div>");
        
        // 文件内容对比
        for (int i = 0; i < filePairs.size(); i++) {
            FilePair pair = filePairs.get(i);
            html.append("<div id=\"tab" + i + "\" class=\"tab-content" + (i == 0 ? " active" : "") + "\">");
            appendFileDiff(html, pair);
            html.append("</div>");
        }
        
        // 脚本和HTML尾部
        html.append("<script>");
        html.append("function openTab(evt, tabName) {");
        html.append("var i, tabContent, tabButtons;");
        html.append("tabContent = document.getElementsByClassName('tab-content');");
        html.append("for (i = 0; i < tabContent.length; i++) {");
        html.append("tabContent[i].classList.remove('active');");
        html.append("}");
        html.append("tabButtons = document.getElementsByClassName('tab-button');");
        html.append("for (i = 0; i < tabButtons.length; i++) {");
        html.append("tabButtons[i].classList.remove('active');");
        html.append("}");
        html.append("document.getElementById(tabName).classList.add('active');");
        html.append("evt.currentTarget.classList.add('active');");
        html.append("}");
        html.append("</script>");
        html.append("</div>");
        html.append("</body>");
        html.append("</html>");
        
        return html.toString();
    }

    /**
     * 添加文件对比内容
     */
    private void appendFileDiff(StringBuilder html, FilePair pair) throws IOException {
        // 读取文件内容
        String sourceContent = FileUtils.readFileToString(pair.getSourceFile(), StandardCharsets.UTF_8);
        String targetContent = FileUtils.readFileToString(pair.getTargetFile(), StandardCharsets.UTF_8);
        
        // 转义HTML特殊字符
        sourceContent = escapeHtml(sourceContent);
        targetContent = escapeHtml(targetContent);
        
        // 代码高亮
        sourceContent = highlightCode(sourceContent);
        targetContent = highlightCode(targetContent);
        
        // 生成差异高亮
        List<String> sourceLines = getLinesWithDiff(sourceContent, targetContent, true);
        List<String> targetLines = getLinesWithDiff(sourceContent, targetContent, false);
        
        html.append("<div class=\"diff-container\">");
        
        // 源文件列
        html.append("<div class=\"diff-column\">");
        html.append("<div class=\"column-header\">源文件 (Oracle): " + pair.getSourceFile().getName() + "</div>");
        html.append("<div class=\"diff-content\">");
        html.append("<pre>");
        for (String line : sourceLines) {
            html.append(line).append("\n");
        }
        html.append("</pre>");
        html.append("</div>");
        html.append("</div>");
        
        // 目标文件列
        html.append("<div class=\"diff-column\">");
        html.append("<div class=\"column-header\">目标文件 (MySQL): " + pair.getTargetFile().getName() + "</div>");
        html.append("<div class=\"diff-content\">");
        html.append("<pre>");
        for (String line : targetLines) {
            html.append(line).append("\n");
        }
        html.append("</pre>");
        html.append("</div>");
        html.append("</div>");
        
        html.append("</div>");
    }

    /**
     * 生成带差异标记的行列表
     */
    private List<String> getLinesWithDiff(String sourceContent, String targetContent, boolean isSource) {
        List<String> result = new ArrayList<>();
        String[] sourceLines = sourceContent.split("\\r?\\n");
        String[] targetLines = targetContent.split("\\r?\\n");
        
        int maxLines = Math.max(sourceLines.length, targetLines.length);
        
        for (int i = 0; i < maxLines; i++) {
            String sourceLine = i < sourceLines.length ? sourceLines[i] : "";
            String targetLine = i < targetLines.length ? targetLines[i] : "";
            
            String line = isSource ? sourceLine : targetLine;
            
            // 如果行内容不同，添加差异标记
            if (!sourceLine.equals(targetLine)) {
                // 如果是整行高亮模式，使用原代码
                // result.add("<span class='changed-line'>" + line + "</span>");
                
                // 新的实现：只高亮行内差异部分
                if (isSource) {
                    // 对于源文件，需要与目标文件比较并标记差异部分
                    result.add(highlightInlineDifferences(sourceLine, targetLine));
                } else {
                    // 对于目标文件，使用相同的方法
                    result.add(highlightInlineDifferences(targetLine, sourceLine));
                }
            } else {
                result.add(line);
            }
        }
        
        return result;
    }
    
    /**
     * 高亮行内的差异部分
     * 简单实现：基于单词的差异标记
     */
    private String highlightInlineDifferences(String line1, String line2) {
        if (line1.equals(line2)) {
            return line1;
        }
        
        // 简单实现：如果两行内容不同，仍然高亮整行
        // 注：完整的行内差异算法需要更复杂的实现，这里保留整行高亮作为基础功能
        return "<span class='changed-line'>" + line1 + "</span>";
    }

    /**
     * 代码高亮
     */
    private String highlightCode(String code) {
        // 重置为更安全的代码高亮实现，避免嵌套标签问题
        
        StringBuilder result = new StringBuilder();
        boolean inString = false;
        boolean inComment = false;
        boolean inIncludeTag = false;
        StringBuilder currentWord = new StringBuilder();
        StringBuilder includeTagBuilder = new StringBuilder();
        
        // 简单的关键字列表
        String[] keywords = {"SELECT", "FROM", "WHERE", "INSERT", "UPDATE", "DELETE", "CREATE", "ALTER", "DROP", "TABLE", "INDEX", "VIEW", "DATABASE", "JOIN", "LEFT", "RIGHT", "INNER", "OUTER", "ON", "AND", "OR", "NOT", "IN", "LIKE", "BETWEEN", "GROUP", "BY", "ORDER", "HAVING", "LIMIT"};
        
        for (int i = 0; i < code.length(); i++) {
            char c = code.charAt(i);
            
            // 检查是否在include标签中
            if (!inString && !inComment && !inIncludeTag && i < code.length() - 8 && 
                code.substring(i, i + 8).equalsIgnoreCase("<include ")) {
                inIncludeTag = true;
                includeTagBuilder.append(c);
                i++;
                
                // 收集整个include标签内容
                while (i < code.length() && ! (i > 0 && code.charAt(i-1) == '>' && code.charAt(i-2) == '/')) {
                    includeTagBuilder.append(code.charAt(i));
                    i++;
                }
                if (i < code.length()) {
                    includeTagBuilder.append(code.charAt(i));
                }
                
                // 保留include标签不做处理
                result.append(includeTagBuilder.toString());
                includeTagBuilder.setLength(0);
                inIncludeTag = false;
                continue;
            }
            
            // 如果在include标签中，继续收集标签内容
            if (inIncludeTag) {
                includeTagBuilder.append(c);
                // 检查标签是否结束
                if (i > 0 && code.charAt(i-1) == '>' && code.charAt(i-2) == '/') {
                    result.append(includeTagBuilder.toString());
                    includeTagBuilder.setLength(0);
                    inIncludeTag = false;
                }
                continue;
            }
            
            // 处理单行注释
            if (!inString && !inIncludeTag && i < code.length() - 1 && c == '-' && code.charAt(i + 1) == '-') {
                if (currentWord.length() > 0) {
                    String word = currentWord.toString();
                    if (isKeyword(word, keywords)) {
                        result.append("<span class='sql-keyword'>").append(word).append("</span>");
                    } else {
                        result.append(word);
                    }
                    currentWord.setLength(0);
                }
                
                // 添加注释
                inComment = true;
                StringBuilder comment = new StringBuilder();
                comment.append(c).append(code.charAt(i + 1));
                i += 2;
                
                while (i < code.length() && code.charAt(i) != '\n') {
                    comment.append(code.charAt(i));
                    i++;
                }
                
                result.append("<span class='sql-comment'>").append(comment).append("</span>");
                if (i < code.length()) {
                    result.append('\n');
                }
                inComment = false;
                continue;
            }
            
            // 处理字符串
            if (!inComment && !inIncludeTag && c == '\'') {
                if (currentWord.length() > 0) {
                    String word = currentWord.toString();
                    if (isKeyword(word, keywords)) {
                        result.append("<span class='sql-keyword'>").append(word).append("</span>");
                    } else {
                        result.append(word);
                    }
                    currentWord.setLength(0);
                }
                
                inString = !inString;
                StringBuilder string = new StringBuilder();
                string.append(c);
                i++;
                
                while (i < code.length() && code.charAt(i) != '\'' && !(i > 0 && code.charAt(i-1) == '\'' && code.charAt(i) == '\'')) {
                    string.append(code.charAt(i));
                    i++;
                }
                if (i < code.length()) {
                    string.append(code.charAt(i));
                }
                
                result.append("<span class='sql-string'>").append(string).append("</span>");
                continue;
            }
            
            // 处理关键字和普通文本
            if (!inComment && !inString && !inIncludeTag) {
                if (Character.isWhitespace(c) || ",;(){}[]=<>+".indexOf(c) != -1) {
                    if (currentWord.length() > 0) {
                        String word = currentWord.toString();
                        if (isKeyword(word, keywords)) {
                            result.append("<span class='sql-keyword'>").append(word).append("</span>");
                        } else {
                            result.append(word);
                        }
                        currentWord.setLength(0);
                    }
                    result.append(c);
                } else {
                    currentWord.append(c);
                }
            } else {
                // 在字符串或注释中，直接添加字符
                result.append(c);
            }
        }
        
        // 处理最后一个单词
        if (currentWord.length() > 0) {
            String word = currentWord.toString();
            if (isKeyword(word, keywords)) {
                result.append("<span class='sql-keyword'>").append(word).append("</span>");
            } else {
                result.append(word);
            }
        }
        
        return result.toString();
    }
    
    /**
     * 检查是否为SQL关键字
     */
    private boolean isKeyword(String word, String[] keywords) {
        for (String keyword : keywords) {
            if (keyword.equalsIgnoreCase(word)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 转义HTML特殊字符
     */
    private String escapeHtml(String input) {
        return input
            .replace("&", "&amp;")
            .replace("<", "&lt;")
            .replace(">", "&gt;")
            .replace("\"", "&quot;")
            .replace("'", "&#39;");
    }

    /**
     * 文件对类，用于存储源文件和目标文件的对应关系
     */
    public static class FilePair {
        private File sourceFile;
        private File targetFile;
        
        public FilePair(File sourceFile, File targetFile) {
            this.sourceFile = sourceFile;
            this.targetFile = targetFile;
        }
        
        public File getSourceFile() {
            return sourceFile;
        }
        
        public File getTargetFile() {
            return targetFile;
        }
    }
}